package 
main;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import stripsLanguage.Binding;
import stripsLanguage.Instance;
import stripsLanguage.Link;
import stripsLanguage.Operator;
import stripsLanguage.Ordering;
import stripsLanguage.Plan;
import stripsLanguage.Predicate;
import stripsLanguage.PredicateType;
import stripsLanguage.Step;
import stripsLanguage.Task;

public class TaskFromXML {

	private static final int RODS = 3;
	
	public Task createTaskFromXmlFile(File path) throws DocumentException
	{	
		SAXReader reader = new SAXReader();
		Document xmlDocument = reader.read(path);
		
		Element taskElement = xmlDocument.getRootElement();
		
		if (!taskElement.getName().equals("strips"))
			throw new DocumentException();
		
		PathToOutput.getInstance().setPath(new File(((Element) (taskElement.element("options")
				.elements()).get(0)).attribute("path").getValue()));
		
		if( ((Element) (taskElement.element("options").elements()).get(0))
			.attribute("option").getValue().compareTo("generate")== 0 )			
				return generateHanoiTowersTask(taskElement);
		
		return loadTaskFromXmlFile(taskElement);
	}
		
	public Task generateHanoiTowersTask(Element taskElement)
	{
		int ON = 1;
		int SMALLER = 2;		
		int CLEAR = 3;

		int ARGS_ON = 2;
		int ARGS_SMALLER = 2;		
		int ARGS_CLEAR = 1;
		
		List<Instance> instances = new ArrayList<Instance>();
		List<PredicateType> predicates = new ArrayList<PredicateType>();
		List<Operator> operators = new ArrayList<Operator>();	
				
		int nrOfDisks = Integer.valueOf(
			((Element)taskElement.element("nrOfDisks").elements().get(0)).attribute("nr").getValue());
		int rodFirst = Integer.valueOf(
				((Element)taskElement.element("nrsOfRods").elements().get(0)).attribute("firstRod").getValue());
		int rodLast = Integer.valueOf(
				((Element)taskElement.element("nrsOfRods").elements().get(0)).attribute("lastRod").getValue());;
		
		int id = 1;
		/** generate instances **/
		for(int i = 1; i <= nrOfDisks ;i++)
			instances.add(new Instance("disk" + i, id++));
		for(int i = 1; i <= 3 ;i++)
			instances.add(new Instance("rod" + i,id++));
		
		/** generate types of predicates **/
		predicates.add(new PredicateType(ON, "ON", ARGS_ON));
		predicates.add(new PredicateType(SMALLER, "SMALLER",ARGS_SMALLER));
		predicates.add(new PredicateType(CLEAR, "CLEAR", ARGS_CLEAR));
		
		// generate preconditions for MOVE
		List<Predicate> preconditions = new ArrayList<Predicate>();
		
		Predicate firstCLEAR = (new Predicate(0, CLEAR, "CLEAR", ARGS_CLEAR));
		firstCLEAR.addArgument(1, null);
		preconditions.add(firstCLEAR);
	
		Predicate lastCLEAR = (new Predicate(0, CLEAR, "CLEAR", ARGS_CLEAR));
		lastCLEAR.addArgument(2, null);
		preconditions.add(lastCLEAR);
		
		Predicate on = (new Predicate(0, ON, "ON", ARGS_ON));
		on.addArgument(1, null); on.addArgument(3, null);
		preconditions.add(on);
		
		Predicate smaller = new Predicate(0, SMALLER, "SMALLER", ARGS_SMALLER);
		smaller.addArgument(1, null); smaller.addArgument(2, null);
		preconditions.add(smaller);
		
		// generate effects for MOVE
		List<Predicate> effects = new ArrayList<Predicate>();
		
		Predicate now3CLEAR = (new Predicate(0, CLEAR, "CLEAR", ARGS_CLEAR));
		now3CLEAR.addArgument(3, null);
		effects.add(now3CLEAR);
	
		Predicate unCLEAR = (new Predicate(0, CLEAR, "CLEAR", ARGS_CLEAR));
		unCLEAR.addArgument(2, null);
		unCLEAR.setNegation();
		effects.add(unCLEAR);
		
		Predicate newOn = (new Predicate(0, ON, "ON", ARGS_ON));
		newOn.addArgument(1, null); newOn.addArgument(2, null);
		effects.add(newOn);
		
		Predicate untrueOn = new Predicate(0, ON, "ON", ARGS_ON);
		untrueOn.addArgument(1, null); untrueOn.addArgument(3, null);
		untrueOn.setNegation();
		effects.add(untrueOn);
		
		operators.add(new Operator(3,"MOVE", preconditions,effects));

		/** generate operators **/
		operators.add(new Operator(1, "START", null, null));
		operators.add(new Operator(2, "FINISH", null, null));
		
		// generate start conditions
		
		int idOfPredicate = 1;
		
		// generate SMALLERs
		effects = new ArrayList<Predicate>();
		for(int i = 1 ; i <= nrOfDisks; i++)
			for(int j = i + 1; j <= id-1; j++)
			{
				Predicate p = new Predicate(idOfPredicate++, SMALLER, "SMALLER",ARGS_SMALLER);
				p.addArgument(i, "disk"+i);
				if( j <= nrOfDisks)
					p.addArgument(j, "disk"+j);
				else
					p.addArgument(j, "rod"+(j-nrOfDisks));
				effects.add(p);
			}
		
		// generate on
		for(int i = 1; i < nrOfDisks; i++)
		{
			Predicate p = new Predicate(idOfPredicate++, ON, "ON", ARGS_ON);
			p.addArgument(i, "disk"+i); p.addArgument(i+1, "disk"+(i+1));
			effects.add(p);
		}
		Predicate rod = new Predicate(idOfPredicate++, ON, "ON", ARGS_ON);
		rod.addArgument(nrOfDisks, "disk"+nrOfDisks);
		rod.addArgument(rodFirst+nrOfDisks, "rod"+rodFirst);
		effects.add(rod);

		// generate clear
		for(int i = 1; i <= RODS; i++)
		{
			if ( i != rodFirst)
			{
				Predicate p = new Predicate(idOfPredicate++, CLEAR,"CLEAR", ARGS_CLEAR);
				p.addArgument(i+nrOfDisks, "rod"+i);
				effects.add(p);
			}
		}
		Predicate diskSmallest = new Predicate(idOfPredicate++, CLEAR, "CLEAR", ARGS_CLEAR);
		diskSmallest.addArgument(1, "disk1");
		effects.add(diskSmallest);
		
		Step start = new Step(1,1, "START",new ArrayList<Predicate>(),effects);
		 
		// generate finish conditions
		preconditions = new ArrayList<Predicate>();
		// generate on
		for(int i = 1; i < nrOfDisks; i++)
		{
			Predicate p = new Predicate(idOfPredicate++, ON, "ON", ARGS_ON);
			p.addArgument(i, "disk"+i); p.addArgument(i+1, "disk"+(i+1));
			preconditions.add(p);
		}
		Predicate p = new Predicate(idOfPredicate++, CLEAR, "CLEAR", ARGS_CLEAR);
		p.addArgument(nrOfDisks+1, "rod1");
		preconditions.add(p);
		p = new Predicate(idOfPredicate++, CLEAR, "CLEAR", ARGS_CLEAR);
		p.addArgument(nrOfDisks+2, "rod2");
		preconditions.add(p);
		
		rod = new Predicate(idOfPredicate++, ON, "ON", ARGS_ON);
		rod.addArgument(nrOfDisks, "disk"+nrOfDisks);
		rod.addArgument(rodLast+nrOfDisks, "rod"+rodLast);
		preconditions.add(rod);
		 
		Step finish = new Step(2,2,"FINISH",preconditions,new ArrayList<Predicate>());
		
		List<Step> steps = new ArrayList<Step>();
		steps.add(start);
		steps.add(finish);
		
		List<Ordering> orderings = new ArrayList<Ordering>();
		orderings.add(new Ordering(start, finish));
		
		List<Link> links = new ArrayList<Link>();
		
		Plan firstPlan = new Plan(
			steps,
			links,
			orderings,
			new ArrayList<Binding>());
		
		return new Task(
			instances,
			predicates,
			operators,
			firstPlan);		
	}
	
	@SuppressWarnings("unchecked")
	public Task loadTaskFromXmlFile(Element taskElement)
	{
		List<Instance> instances = new ArrayList<Instance>();
		List<PredicateType> predicates = new ArrayList<PredicateType>();
		List<Operator> operators = new ArrayList<Operator>();	
		
		/** read the instances **/
		List instanceElements = taskElement.element("instances").elements();
		int id = 1;
		for (Object instanceElement: instanceElements)
			instances.add(new Instance(
				((Element) instanceElement).attribute("name").getValue(),
				id++));
		
		/** read the predicates **/
		List predicateElements = taskElement.element("predicates").elements();
		id = 1;
		for (Object predicateElement: predicateElements)
			predicates.add(new PredicateType(
				id++,
				((Element) predicateElement).attribute("name").getValue(),
				Integer.valueOf(((Element) predicateElement).attribute("nrOfArgs").getValue())));
		
		/** read the operators **/
		id = 1;
		for(Object operatorElement: taskElement.element("operators").elements())
		{
			//String operatorName = ((Element) operatorElement).attribute("name").getValue();
			List<Predicate> preconditions = new ArrayList<Predicate>();		
			for(Object preconditionElement: ((Element)operatorElement).element("preconditions").elements())
			{				
				// get id of type of predicate
				int predId = findPredicateId(((Element) preconditionElement).attribute("name")
						.getValue(),predicates);
				Predicate predicate = new Predicate(
					0,
					predId,
					predicates.get(predId-1).getName(),
					predicates.get(predId-1).getNrOfArgs()
					);			
									
				for(Object argumentElement: ((Element)preconditionElement).element("arguments").elements())
					predicate.addArgument(
						Integer.valueOf(((Element) argumentElement).attribute("id").getValue()),
						null);
				preconditions.add(predicate);
			}
			
			List<Predicate> effects = new ArrayList<Predicate>();						
			for(Object effectElement: ((Element)operatorElement).element("effects").elements())
			{	
				// get id of type of predicate
				int predId = findPredicateId(((Element) effectElement).attribute("name")
						.getValue(),predicates);
				
				Predicate predicate = new Predicate(
					0,
					predId,
					predicates.get(predId-1).getName(),
					predicates.get(predId-1).getNrOfArgs());
				// check if predicate sets value to untrue 
				if(Integer.valueOf(((Element) effectElement).attribute("value").getValue()) == -1)
					predicate.setNegation();
															
				for(Object argumentElement: ((Element)effectElement).element("arguments").elements())
					predicate.addArgument(
						Integer.valueOf(((Element) argumentElement).attribute("id").getValue()),
						null);				
				effects.add(predicate);
			}
			
			operators.add(new Operator(
				id++,
				//operatorName,
				((Element) operatorElement).attribute("name").getValue(),
				preconditions,				
				effects));
		}				
		
		int idOfPredicate = 1;
		/** read start condition **/
		List<Predicate> effects = new ArrayList<Predicate>();
		for(Object effectElement: taskElement.element("start").elements())
		{
			// get id of type of predicate
			int predId = findPredicateId(((Element) effectElement).attribute("name")
					.getValue(),predicates);
			Predicate predicate = new Predicate(
				idOfPredicate++,
				predId,
				predicates.get(predId-1).getName(),
				predicates.get(predId-1).getNrOfArgs());
					
			for(Object argumentElement: ((Element)effectElement).element("arguments").elements())
			{
				int instanceId = findInstanceId(
						((Element) argumentElement).attribute("name").getValue(),
						instances);
				predicate.addArgument(
					instanceId,
					instances.get(instanceId-1).getName());
			}
			effects.add(predicate);
		}
		
		operators.add(new Operator(id, "Start", null, null));
		Step start = new Step(1,id++,"Start",new ArrayList<Predicate>(),effects);
		
		/** read finish condition **/
		List<Predicate> preconditions = new ArrayList<Predicate>();
		for(Object preconditionElement: taskElement.element("finish").elements())
		{
			int predId = findPredicateId( // get id of type of predicate
					((Element) preconditionElement).attribute("name").getValue(),predicates);
			Predicate predicate = new Predicate(
				idOfPredicate++,
				predId,
				predicates.get(predId-1).getName(),
				predicates.get(predId-1).getNrOfArgs());
						
			for(Object argumentElement: ((Element)preconditionElement).element("arguments").elements())
			{
				int instanceId = findInstanceId(
						((Element) argumentElement).attribute("name").getValue(),
						instances);
				predicate.addArgument(
					instanceId,
					instances.get(instanceId-1).getName());
			}
			preconditions.add(predicate);
		}	

		operators.add(new Operator(id,"Finish",null,null));
		Step finish = new Step(2,id++, "Finish",preconditions,new ArrayList<Predicate>());	
		
		List<Step> steps = new ArrayList<Step>();
		steps.add(start);
		steps.add(finish);
		
		List<Ordering> orderings = new ArrayList<Ordering>();
		orderings.add(new Ordering(start, finish));
		
		List<Link> links = new ArrayList<Link>();
		
		Plan firstPlan = new Plan(
			steps,
			links,
			orderings,
			new ArrayList<Binding>());
		
		return new Task(
			instances,
			predicates,
			operators,
			firstPlan);	
	}
	
	public TaskFromXML(){}
	
	public int findInstanceId(String name, List<Instance> instances)
	{
		for(Instance ins: instances)
			if(name.compareTo(ins.getName()) == 0)
				return ins.getId();
		// TODO throw Exception
		return 0;
	}
	
	public int findPredicateId(String name, List<PredicateType> predicates)
	{
		for(PredicateType p: predicates)
			if(name.compareTo(p.getName()) == 0)
				return p.getNrOfType();
		// TODO throw Exception
		return 0;
	}
}
